/*
 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package com.sun.corba.se.impl.encoding;

import java.util.Hashtable;
import com.sun.org.omg.CORBA.ValueDefPackage.FullValueDescription;
import com.sun.org.omg.SendingContext.CodeBase;
import com.sun.org.omg.SendingContext.CodeBaseHelper;
import com.sun.org.omg.SendingContext._CodeBaseImplBase;
import com.sun.org.omg.SendingContext._CodeBaseStub;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.orb.ORB;

/**
 * Provides the reading side with a per connection cache of
 * info obtained via calls to the remote CodeBase.
 *
 * Previously, most of this was in IIOPConnection.
 *
 * Features:
 * Delays cache creation unless used
 * Postpones remote calls until necessary
 * Handles creating obj ref from IOR
 * Maintains caches for the following maps:
 * CodeBase IOR to obj ref (global)
 * RepId to implementation URL(s)
 * RepId to remote FVD
 * RepId to superclass type list
 *
 * Needs cache management.
 */
public class CachedCodeBase extends _CodeBaseImplBase {

  private Hashtable implementations, fvds, bases;
  private volatile CodeBase delegate;
  private CorbaConnection conn;

  private static Object iorMapLock = new Object();
  private static Hashtable<IOR, CodeBase> iorMap = new Hashtable<>();

  public static synchronized void cleanCache(ORB orb) {
    synchronized (iorMapLock) {
      for (IOR ior : iorMap.keySet()) {
        if (ior.getORB() == orb) {
          iorMap.remove(ior);
        }
      }
    }
  }

  public CachedCodeBase(CorbaConnection connection) {
    conn = connection;
  }

  public com.sun.org.omg.CORBA.Repository get_ir() {
    return null;
  }

  public synchronized String implementation(String repId) {
    String urlResult = null;

    if (implementations == null) {
      implementations = new Hashtable();
    } else {
      urlResult = (String) implementations.get(repId);
    }

    if (urlResult == null && connectedCodeBase()) {
      urlResult = delegate.implementation(repId);

      if (urlResult != null) {
        implementations.put(repId, urlResult);
      }
    }

    return urlResult;
  }

  public synchronized String[] implementations(String[] repIds) {
    String[] urlResults = new String[repIds.length];

    for (int i = 0; i < urlResults.length; i++) {
      urlResults[i] = implementation(repIds[i]);
    }

    return urlResults;
  }

  public synchronized FullValueDescription meta(String repId) {
    FullValueDescription result = null;

    if (fvds == null) {
      fvds = new Hashtable();
    } else {
      result = (FullValueDescription) fvds.get(repId);
    }

    if (result == null && connectedCodeBase()) {
      result = delegate.meta(repId);

      if (result != null) {
        fvds.put(repId, result);
      }
    }

    return result;
  }

  public synchronized FullValueDescription[] metas(String[] repIds) {
    FullValueDescription[] results
        = new FullValueDescription[repIds.length];

    for (int i = 0; i < results.length; i++) {
      results[i] = meta(repIds[i]);
    }

    return results;
  }

  public synchronized String[] bases(String repId) {

    String[] results = null;

    if (bases == null) {
      bases = new Hashtable();
    } else {
      results = (String[]) bases.get(repId);
    }

    if (results == null && connectedCodeBase()) {
      results = delegate.bases(repId);

      if (results != null) {
        bases.put(repId, results);
      }
    }

    return results;
  }

  // Ensures that we've used the connection's IOR to create
  // a valid CodeBase delegate.  If this returns false, then
  // it is not valid to access the delegate.
  private synchronized boolean connectedCodeBase() {
    if (delegate != null) {
      return true;
    }

    // The delegate was null, so see if the connection's
    // IOR was set.  If so, then we just need to connect
    // it.  Otherwise, there is no hope of checking the
    // remote code base.  That could be bug if the
    // service context processing didn't occur, or it
    // could be that we're talking to a foreign ORB which
    // doesn't include this optional service context.
    if (conn.getCodeBaseIOR() == null) {
      // REVISIT.  Use Merlin logging service to report that
      // codebase functionality was requested but unavailable.
      if (conn.getBroker().transportDebugFlag) {
        conn.dprint("CodeBase unavailable on connection: " + conn);
      }

      return false;
    }

    synchronized (iorMapLock) {

      // Recheck the condition to make sure another
      // thread didn't already do this while we waited
      if (delegate != null) {
        return true;
      }

      // Do we have a reference initialized by another connection?
      delegate = CachedCodeBase.iorMap.get(conn.getCodeBaseIOR());

      if (delegate != null) {
        return true;
      }

      // Connect the delegate and update the cache
      delegate = CodeBaseHelper.narrow(getObjectFromIOR());

      // Save it for the benefit of other connections
      CachedCodeBase.iorMap.put(conn.getCodeBaseIOR(), delegate);
    }

    // It's now safe to use the delegate
    return true;
  }

  private final org.omg.CORBA.Object getObjectFromIOR() {
    return CDRInputStream_1_0.internalIORToObject(
        conn.getCodeBaseIOR(), null /*stubFactory*/, conn.getBroker());
  }
}

// End of file.
